package org.class2table.core.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import org.class2table.core.clazz.bean.ClazzMetaData;
import org.class2table.core.clazz.bean.PropertyMetaData;
import org.class2table.core.parent.bean.PrimaryKeyInfo;
import org.class2table.core.dictionary.Constants;
import org.class2table.core.dictionary.DefaultRule;
import org.class2table.core.template.SqlTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

public class SqlBuildUtil {

    private static class PartUtil {
        public static String getIsNullable(PropertyMetaData clazzPropertyMetaData) {
            return clazzPropertyMetaData.getIsNullable() ?
                    Constants.SQL.KEYWORD.NULLABLE.NULL : Constants.SQL.KEYWORD.NULLABLE.NOT_NULL;
        }

        public static String getDefault(PropertyMetaData clazzPropertyMetaData) {
            Boolean isSetDefault = clazzPropertyMetaData.getIsSetDefault();
            String columnType = clazzPropertyMetaData.getColumnType();
            String columnDefault = clazzPropertyMetaData.getColumnDefault();
            if (!isSetDefault) {
                return Constants.COMMON.blankStr;
            }
            Optional<String> first = DefaultRule.SqlDefaultRule.TEXT_TYPE.
                    stream().filter(columnType::contains).findFirst();
            if (first.isPresent()) {
                columnDefault = StringOperationUtil.addApostrophe(columnDefault);
            }
            return Constants.SQL.KEYWORD.DEFAULT.DEFAULT + Constants.COMMON.blankSpace + columnDefault;
        }

        public static String getComment(PropertyMetaData clazzPropertyMetaData) {
            String comment = clazzPropertyMetaData.getColumnComment();
            return Constants.SQL.KEYWORD.COMMENT.COMMENT + Constants.COMMON.blankSpace + StringOperationUtil.addApostrophe(comment);
        }
    }

    private static class DefineUtil {
        public static String getColumnDefine(PropertyMetaData propertyMetaData) {
            String columnName = propertyMetaData.getColumnName();
            String columnType = propertyMetaData.getColumnType();
            String columnIsNullable = PartUtil.getIsNullable(propertyMetaData);
            String columnDefault = PartUtil.getDefault(propertyMetaData);
            String comment = PartUtil.getComment(propertyMetaData);
            return String.format(SqlTemplate.Define.COLUMN_DEFINE_TEMPLATE,
                    columnName,
                    columnType,
                    columnIsNullable,
                    columnDefault,
                    comment);
        }
        public static String getPrimaryKeyDefine(List<PrimaryKeyInfo> primaryKeyInfoList) {
            if (CollectionUtil.isEmpty(primaryKeyInfoList)) {
                return Constants.COMMON.blankStr;
            }
            StringBuilder primaryKeyColumns = new StringBuilder();
            for (PrimaryKeyInfo primaryKeyInfo : primaryKeyInfoList) {
                String columnName = primaryKeyInfo.getColumnName();
                primaryKeyColumns.append(StringOperationUtil.addBackQuote(columnName)).append(Constants.COMMON.comma);
            }
            primaryKeyColumns.deleteCharAt(primaryKeyColumns.length() - 1);
            return String.format(SqlTemplate.Define.PRIMARY_KEY_DEFINE_TEMPLATE, primaryKeyColumns.toString());
        }
    }


    public static String buildAddColumnSql(String tableName, PropertyMetaData clazzPropertyMetaData) {
        return String.format(
                SqlTemplate.Operation.ADD_COLUMN_TEMPLATE,
                tableName,
                clazzPropertyMetaData.getColumnName(),
                clazzPropertyMetaData.getColumnType(),
                PartUtil.getIsNullable(clazzPropertyMetaData),
                PartUtil.getDefault(clazzPropertyMetaData),
                PartUtil.getComment(clazzPropertyMetaData)
        );
    }

    public static String buildModifyColumnSql(String tableName, PropertyMetaData clazzPropertyMetaData) {
        return String.format(
                SqlTemplate.Operation.MODIFY_COLUMN_TEMPLATE,
                tableName,
                clazzPropertyMetaData.getColumnName(),
                clazzPropertyMetaData.getColumnType(),
                PartUtil.getIsNullable(clazzPropertyMetaData),
                PartUtil.getDefault(clazzPropertyMetaData),
                PartUtil.getComment(clazzPropertyMetaData)
        );
    }

    public static String buildDropColumnSql(String tableName, String dbColumnName) {
        return String.format(
                SqlTemplate.Operation.DROP_COLUMN_TEMPLATE,
                tableName,
                dbColumnName
        );
    }

    public static String buildCreateTableSql(ClazzMetaData clazzMetaData) {
        List<PrimaryKeyInfo> primaryKeyInfoList = clazzMetaData.getPrimaryKeyInfoList();
        String tableName = clazzMetaData.getTableName();
        List<PropertyMetaData> propertyMetaDataList = clazzMetaData.getEntityAttrMetaDataList();
        String primaryKeyDefine = DefineUtil.getPrimaryKeyDefine(primaryKeyInfoList);
        return String.format(
                SqlTemplate.Operation.CREATE_TABLE_TEMPLATE,
                tableName,
                ((Function<List<PropertyMetaData>, String>) cs -> {
                    StringBuilder allColumns = new StringBuilder();
                    for (PropertyMetaData temp : cs) {
                        String columnDefine = DefineUtil.getColumnDefine(temp);
                        allColumns.append(columnDefine).append(Constants.COMMON.enter).append(Constants.COMMON.comma);
                    }
                    if (StrUtil.isNotEmpty(allColumns)) {
                        allColumns.deleteCharAt(allColumns.length() - 1);
                    }
                    return allColumns.toString();
                }).apply(propertyMetaDataList)
                        +
                        (StrUtil.isEmpty(primaryKeyDefine) ?
                                Constants.COMMON.blankStr
                                : Constants.COMMON.comma + primaryKeyDefine
                        )

        );
    }

    public static String buildDropPrimaryKeySql(String tableName) {
        return String.format(SqlTemplate.Operation.DROP_PRIMARY_KEY_TEMPLATE, tableName);
    }

    public static String buildAddPrimaryKeySql(String tableName, List<PrimaryKeyInfo> clazzPrimaryKeyInfoList) {
        String primaryKeyDefine = DefineUtil.getPrimaryKeyDefine(clazzPrimaryKeyInfoList);
        if (StrUtil.isNotEmpty(primaryKeyDefine)) {
            return String.format(SqlTemplate.Operation.ADD_PRIMARY_KEY_TEMPLATE, tableName, primaryKeyDefine);
        }
        return Constants.COMMON.blankStr;
    }

}
